#ifndef BOLCKQUEUE_H
#define BLOCKQUEUE_H

#include <deque>
#include <condition_variable>
#include <memory>
#include <assert.h>

/*
    阻塞队列，有一个生产者和一个消费者，均为condition_variable类型,
    当入队时，判断队是否已满，producer.wait，入队成功，则consumer.notify_one
    当出队时，判断队是否为空，consumer.wait，出队成功，则producer.notify_one
    flush通知consumer进行消费
*/

template <class T>
class BlockDeque {
public:
    explicit BlockDeque(int maxcapacity = 1000);

    ~BlockDeque(); // 析构函数需要清空队列（加锁）、置isclose为true，condition_variable.notify_all()

    bool Full(); // 判断是否满了

    void Clear();

    void Flush();

    void PushBack(const T& item);

    bool Pop(T &item);

    bool Empty();

    void Close();
private:
    std::deque<T> m_deq; // 实际的双端队列

    size_t m_capacity;

    bool m_isClose;

    std::mutex mtx;

    std::condition_variable m_consumer; // 消费者

    std::condition_variable m_producer; // 生产者
};

template<class T>
BlockDeque<T>::BlockDeque(int maxcapacity) {
    assert(maxcapacity > 0);
    m_isClose = false;
}

template<class T>
BlockDeque<T>::~BlockDeque() {
    Close();
}

template<class T>
void BlockDeque<T>::Close() {
        {
        std::lock_guard<std::mutex> locker(mtx);
        m_deq.clear();
        m_isClose = true;
    }
    m_producer.notify_all();
    m_consumer.notify_all();
}

template<class T>
void BlockDeque<T>::Flush() {
    m_consumer.notify_one();
}

template<class T>
bool BlockDeque<T>::Full() {
    std::lock_guard<std::mutex> locker(mtx);
    return m_deq.size() >= m_capacity;
}

template<class T>
bool BlockDeque<T>::Empty() {
    std::lock_guard<std::mutex> locker(mtx);
    return m_deq.empty();
}

template<class T>
void BlockDeque<T>::PushBack(const T &item) {
    std::unique_lock<std::mutex> locker(mtx);
    while(m_deq.size() >= m_capacity) {
        m_producer.wait(locker);
    }
    m_deq.push_back(item);
    m_consumer.notify_one();
}

template<class T>
bool BlockDeque<T>::Pop(T &item) {
    std::unique_lock<std::mutex> locker(mtx);
    // 当队列为空时，进行等待
    while(m_deq.empty()) {
        m_consumer.wait(locker);
        if (m_isClose) {
            return false;
        }
    }
    item = m_deq.front();
    m_deq.pop_front();
    m_producer.notify_one();
    return true;
}


#endif